﻿using Modbus.Device;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CrankShaftAnalysisSystem
{
    public class ModbusProxy
    {
        private TcpClient client;
        private ModbusIpMaster master;
        private System.Timers.Timer poll_timer; //用于轮训设备状态
        private Task connection_task; //用于判断连接状态以及是否重连
        public string ServerIp { get; set; }
        public int ServerPort { get; set; }
        public int ReadTimeout { get; set; }
        public bool Connected { get; set; } = false;
        public ushort[] RxRegisters { get; private set; }
        public ushort RegisterAdress { get; set; }
        public ushort RegisterLength { get; set; }
        public event Action<ushort[]> OnGetHoldingRegisters;

        public class Message
        {
            public ushort Address { get; set; }
            public ushort[] Regs { get; set; }
        }

        public Queue<Message> TxQueue = new Queue<Message>();
        public int MaxQueueSize = 5;  // 发送队列长度限制

        public ModbusProxy(string ip, int port,int interval=100, int timeout=200)
        {
            client = new TcpClient();
            ServerIp = ip;
            ServerPort = port;
            poll_timer = new System.Timers.Timer()
            {
                AutoReset = true,
                Interval = interval,
                Enabled = false
            };
            ReadTimeout = timeout;
            client.ReceiveTimeout = ReadTimeout;
            client.SendTimeout = timeout;
        }

        private TcpClient CreateClient()
        {
            var ret = new TcpClient()
            {
                ReceiveTimeout = ReadTimeout
            };
            return ret;
        }

        public void begin()
        {
            if(!(connection_task is null))
            {
                throw new Exception("System Already Started.");
            }

            master = ModbusIpMaster.CreateIp(client);
            poll_timer.Elapsed += poll_task;
            poll_timer.Start();
            connection_task = Task.Factory.StartNew(() =>
            {
                while(true)
                {
                    if(!Connected)
                    {
                        try
                        {
                            if (client is null)
                            {
                                client = CreateClient();
                                master = ModbusIpMaster.CreateIp(client);
                            }
                            client.Connect(ServerIp, ServerPort);
                            Console.WriteLine($"Connect {ServerIp}:{ServerPort} Successfully.");
                            Connected = true;
                        }
                        catch (Exception)
                        {
                            CloseTcp();
                        }
                    }
                    Thread.Sleep(1000);
                }
            }, TaskCreationOptions.LongRunning);
        }

        public void CloseTcp()
        {
            Connected = false;
            if (!(client is null))
            {
                client.Close();
                client.Dispose();
            }
            client = null;
        }

        public void SendRegistersAsync(Message msg)
        {
            TxQueue.Enqueue(msg);
        }

        public void SendRegisters(Message msg)
        {
            master.WriteMultipleRegisters(msg.Address, msg.Regs);
        }

        private void poll_task(object sender, EventArgs e)
        {
            if (!Connected)
                return;
            try
            {
                RxRegisters = master.ReadHoldingRegisters(RegisterAdress, RegisterLength);
                if(!(OnGetHoldingRegisters is null))
                {
                    OnGetHoldingRegisters(RxRegisters);
                }
                if (TxQueue.Count > MaxQueueSize) 
                    Console.WriteLine("Tx Queue Full.");
                while (TxQueue.Count > MaxQueueSize)
                {
                    TxQueue.Dequeue();
                }
                if(TxQueue.Count > 0)
                {
                    var msg = TxQueue.Dequeue();
                    master.WriteMultipleRegisters(msg.Address, msg.Regs);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("[MODBUS ERROR]: " + ex.Message);
                CloseTcp();
            }
        }
    }
}
